package org.managarm.jmanaintlib.machine;

import java.util.LinkedList;
import org.managarm.jmanavalib.core.VaMthInfo;

public final class VmState {
	private final LinkedList<VmExecutor> mthStack
			= new LinkedList<VmExecutor>();
	
	public final boolean finished() {
		return mthStack.isEmpty();
	}
	
	public final void singleStep() throws VmError {
		try {
			// process method returns
			final VmExecutor callee = mthStack.getLast();
			if(callee.hasReturned()) {	
				mthStack.removeLast();
				if(mthStack.isEmpty() == false)
					mthStack.getLast().processReturn(callee.getReturn());
			}
			
			// process a single instruction
			if(mthStack.isEmpty() == false)
				mthStack.getLast().singleStep(this);
		} catch(VmError e) {
			throw new VmError("Virtual machine death\n"
					+ "Virtual machine stack:\n"
					+ readableStackTrace(), e);
		}
	}
	
	public final void pushMethod(VmExecutor executor) {
		mthStack.add(executor);
	}
	
	public final void throwException(VmCell exception) throws VmError {
		while(mthStack.isEmpty() == false) {
			final VmExecutor function = mthStack.getLast();
			if(function.catchException(exception))
				return;
			mthStack.removeLast();
		}
		
		throw new VmError("Uncaught exception: "
				+ exception.getClsInfo().ident);
	}
	
	public final String readableStackTrace() {
		final StringBuffer buffer = new StringBuffer();
		for(int i = mthStack.size(); i > 0; i--) {
			final VmCode code = mthStack.get(i - 1).getCodeObject();
			final VaMthInfo mthinfo = code.getMthInfo();
			buffer.append("   " + mthinfo.clsInfo.ident
					+ ":" + mthinfo.ident + "(");
			if(mthinfo.getArgumentCnt() > 0)
				buffer.append(mthinfo.getArgument(0));
			for(int j = 1; j < mthinfo.getArgumentCnt(); j++)
				buffer.append(", " + mthinfo.getArgument(j));
			buffer.append(")");
			if(i != 1)
				buffer.append("\n");
		}
		return buffer.toString();
	}
}
